/*
 * Copyright (c) 2024 Black Sesame Technologies
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <gtest/gtest.h>
#include "dal/config_loader.h"
#include "dal/qos_policy.h"
#include "dal/domain_participant.h"
#include "dal/publisher.h"
#include "dal_fastdds/converter/QosConverter.hpp"
#include "dal_fastdds/entity_impl/DalDataWriterImpl.hpp"
#include "dal_fastdds/entity_impl/DalDataReaderImpl.hpp"
#include "dal_fastdds/entity_impl/DalPublisherImpl.hpp"
#include "dal_fastdds/entity_impl/DalSubscriberImpl.hpp"
#include "dal_fastdds/entity_impl/DalTopicImpl.hpp"
#include "dal_fastdds/entity_impl/DalDomainParticipantImpl.hpp"
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include "test_resource/HelloWorldPubSubTypes.h"
#include "dal_fastdds/config_manager/ConfigManager.hpp"
#include "mj_interfaces/msg/mjtype.hpp"

using namespace eprosima::fastdds::dds;

TEST(config_loader_participant_qos_policy_check, test_config_loader)
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    ret = dal_load_config_from_path("fastdds_profiles.xml", nullptr);
    EXPECT_EQ(ret, DAL_RETCODE_OK);
    EXPECT_EQ(ConfigManager::GetInstance()->getConfigType(),
              ConfigManager::ConfigType::CONFIG_TYPE_SET_BY_API);

    DomainParticipantQos participant_qos;
    DomainParticipantFactory::get_instance()->get_participant_qos_from_profile(
        "participant_profile_01", participant_qos);

    EXPECT_EQ(participant_qos.name(), "TestParticipant_01");
    EXPECT_EQ(participant_qos.transport().send_socket_buffer_size, 8192);
    EXPECT_EQ(participant_qos.transport().listen_socket_buffer_size, 1024);
    EXPECT_EQ(participant_qos.wire_protocol().builtin.discovery_config.discoveryProtocol,
              eprosima::fastrtps::rtps::DiscoveryProtocol_t::SIMPLE);
    EXPECT_EQ(participant_qos.wire_protocol()
                  .builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol,
              true);
    EXPECT_EQ(participant_qos.wire_protocol()
                  .builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter,
              true);
    EXPECT_EQ(participant_qos.wire_protocol()
                  .builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter,
              true);
    EXPECT_EQ(participant_qos.wire_protocol().builtin.discovery_config.leaseDuration.seconds, 12);
    EXPECT_EQ(participant_qos.wire_protocol()
                  .builtin.discovery_config.leaseDuration_announcementperiod.seconds,
              3);
    EXPECT_EQ(participant_qos.wire_protocol().port.domainIDGain, 100);
    EXPECT_EQ(participant_qos.wire_protocol().port.portBase, 20000);

    EXPECT_EQ(participant_qos.wire_protocol().default_unicast_locator_list.size(), 1);
    EXPECT_EQ(participant_qos.wire_protocol().builtin.metatrafficMulticastLocatorList.size(), 2);
    EXPECT_EQ(participant_qos.wire_protocol().builtin.metatrafficMulticastLocatorList.isValid(),
              true);
    EXPECT_EQ(participant_qos.wire_protocol().builtin.metatrafficUnicastLocatorList.size(), 2);
    EXPECT_EQ(participant_qos.wire_protocol().builtin.metatrafficUnicastLocatorList.isValid(),
              true);
}

TEST(config_loader_writer_reader_qos_policy_check, test_config_loader)
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    DomainParticipant* participant_;
    Publisher* publisher_;
    Subscriber* subscriber_;
    Topic* topic_;
    DataWriterQos writer_qos;
    DataReaderQos reader_qos;
    TypeSupport* type_ = new TypeSupport(new HelloWorldPubSubType());

    ret = dal_load_config_from_path("fastdds_profiles.xml", nullptr);
    EXPECT_EQ(ret, DAL_RETCODE_OK);
    EXPECT_EQ(ConfigManager::GetInstance()->getConfigType(),
              ConfigManager::ConfigType::CONFIG_TYPE_SET_BY_API);

    DomainParticipantQos f_qos;
    participant_ = DomainParticipantFactory::get_instance()->create_participant(0, f_qos);
    EXPECT_NE(participant_, nullptr);

    type_->register_type(participant_);
    topic_ = participant_->create_topic("HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT);
    EXPECT_NE(topic_, nullptr);

    publisher_ = participant_->create_publisher(PUBLISHER_QOS_DEFAULT, nullptr);
    EXPECT_NE(publisher_, nullptr);
    subscriber_ = participant_->create_subscriber(SUBSCRIBER_QOS_DEFAULT, nullptr);
    EXPECT_NE(subscriber_, nullptr);

    publisher_->get_datawriter_qos_from_profile("datawriter_profile_01", writer_qos);
    subscriber_->get_datareader_qos_from_profile("datareader_profile_01", reader_qos);

    EXPECT_EQ(writer_qos.resource_limits().max_samples, 10000);
    EXPECT_EQ(writer_qos.history().depth, 2);

    EXPECT_EQ(writer_qos.history().kind, eprosima::fastrtps::KEEP_LAST_HISTORY_QOS);
    EXPECT_EQ(writer_qos.durability().kind, eprosima::fastrtps::VOLATILE_DURABILITY_QOS);

    EXPECT_EQ(writer_qos.publish_mode().kind, eprosima::fastrtps::ASYNCHRONOUS_PUBLISH_MODE);
    EXPECT_EQ(writer_qos.endpoint().history_memory_policy,
              eprosima::fastrtps::rtps::MemoryManagementPolicy::DYNAMIC_RESERVE_MEMORY_MODE);

    EXPECT_EQ(writer_qos.reliable_writer_qos().times.heartbeatPeriod.seconds, 1);
    EXPECT_EQ(writer_qos.reliable_writer_qos().times.heartbeatPeriod.nanosec, 0);

    EXPECT_EQ(reader_qos.durability().kind, eprosima::fastrtps::VOLATILE_DURABILITY_QOS);
    EXPECT_EQ(reader_qos.history().depth, 2);
    EXPECT_EQ(reader_qos.resource_limits().max_samples, 10000);
    EXPECT_EQ(reader_qos.endpoint().history_memory_policy,
              eprosima::fastrtps::rtps::MemoryManagementPolicy::DYNAMIC_RESERVE_MEMORY_MODE);

    participant_->delete_topic(topic_);
    participant_->delete_publisher(publisher_);
    participant_->delete_subscriber(subscriber_);
    DomainParticipantFactory::get_instance()->delete_participant(participant_);
}

TEST(config_loader_flow_controller_check, test_config_loader)
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    DomainParticipant* participant_;

    ret = dal_load_config_from_path("fastdds_profiles.xml", nullptr);
    EXPECT_EQ(ret, DAL_RETCODE_OK);

    dal_domain_participant_qos_t* d_qos = dal_create_default_participant_qos();
    DomainParticipantQos participantQos;

    auto converter = QosConverter::GetInstance();
    ret = converter->ConvertParticipantQosFromDalToFastdds(d_qos, &participantQos);
    EXPECT_EQ(ret, DAL_RETCODE_OK);
    EXPECT_EQ(participantQos.flow_controllers().size(), 1);

    auto flow_controller = participantQos.flow_controllers()[0];
    EXPECT_STREQ(flow_controller->name, "my_flow_controller");
    EXPECT_EQ(flow_controller->scheduler,
              eprosima::fastdds::rtps::FlowControllerSchedulerPolicy::HIGH_PRIORITY);
    EXPECT_EQ(flow_controller->max_bytes_per_period, 117464);
    EXPECT_EQ(flow_controller->period_ms, 10);
}

TEST(config_loader_wrong_path_in_api_but_right_env, test_config_loader)
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    ConfigManager::GetInstance()->setConfigType(ConfigManager::ConfigType::CONFIG_TYPE_NOT_SET);

    ret = dal_load_config_from_path("fastdds_profiles_wrong.xml", nullptr);
    EXPECT_EQ(ret, DAL_RETCODE_ERROR);
    EXPECT_EQ(ConfigManager::GetInstance()->getConfigType(),
              ConfigManager::ConfigType::CONFIG_TYPE_NOT_SET);

    setenv("DAL_CONFIG_PATH", "fastdds_profiles.xml", 1);
    ret = dal_load_config_from_path("fastdds_profiles_wrong.xml", nullptr);
    EXPECT_EQ(ret, DAL_RETCODE_OK);
    EXPECT_EQ(ConfigManager::GetInstance()->getConfigType(),
              ConfigManager::ConfigType::CONFIG_TYPE_SET_BY_ENV);
}

static void test_config_loader()
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    // participant
    dal_domain_participant_t* participant =
        dal_create_domain_participant(20, DAL_PARTICIPANT_QOS_DEFAULT, NULL, DAL_ANY_STATUS);

    dal_domain_participant_t* participant_2 = dal_create_domain_participant_with_profile(
        20, DAL_PARTICIPANT_QOS_DEFAULT, "participant_profile_aaa", NULL, DAL_ANY_STATUS);

    auto part_impl = reinterpret_cast<DalDomainParticipantImpl*>(participant->handle_);
    auto f_part = part_impl->FastddsDomainParticipant();
    auto part_impl_2 = reinterpret_cast<DalDomainParticipantImpl*>(participant_2->handle_);
    auto f_part_2 = part_impl_2->FastddsDomainParticipant();

    DomainParticipantQos f_part_qos;
    DomainParticipantQos f_part_qos_2;
    f_part->get_qos(f_part_qos);
    f_part_2->get_qos(f_part_qos_2);

    EXPECT_EQ(f_part_qos.user_data().data_vec().size(), 0);
    EXPECT_NE(f_part_qos_2.user_data().data_vec().size(), 0);

    // publisher
    dal_publisher_t* pub = dal_domain_participant_create_publisher(
        participant, DAL_PUBLISHER_QOS_DEFAULT, NULL, DAL_ANY_STATUS);

    dal_publisher_t* pub_2 = dal_domain_participant_create_publisher_with_profile(
        participant, DAL_PUBLISHER_QOS_DEFAULT, "publisher_profile_aaa", NULL, DAL_ANY_STATUS);

    auto pub_impl = reinterpret_cast<DalPublisherImpl*>(pub->handle_);
    auto f_pub = pub_impl->FastddsPublisher();
    auto pub_impl_2 = reinterpret_cast<DalPublisherImpl*>(pub_2->handle_);
    auto f_pub_2 = pub_impl_2->FastddsPublisher();

    PublisherQos f_pub_qos;
    PublisherQos f_pub_qos_2;
    f_pub->get_qos(f_pub_qos);
    f_pub_2->get_qos(f_pub_qos_2);

    EXPECT_EQ(f_pub_qos.partition().size(), 0);
    EXPECT_EQ(f_pub_qos_2.partition().size(), 2);

    // subscriber
    dal_subscriber_t* sub = dal_domain_participant_create_subscriber(
        participant, DAL_SUBSCRIBER_QOS_DEFAULT, NULL, DAL_ANY_STATUS);

    dal_subscriber_t* sub_2 = dal_domain_participant_create_subscriber_with_profile(
        participant, DAL_SUBSCRIBER_QOS_DEFAULT, "subscriber_profile_aaa", NULL, DAL_ANY_STATUS);

    auto sub_impl = reinterpret_cast<DalSubscriberImpl*>(sub->handle_);
    auto f_sub = sub_impl->FastddsSubscriber();
    auto sub_impl_2 = reinterpret_cast<DalSubscriberImpl*>(sub_2->handle_);
    auto f_sub_2 = sub_impl_2->FastddsSubscriber();

    SubscriberQos f_sub_qos;
    SubscriberQos f_sub_qos_2;
    f_sub->get_qos(f_sub_qos);
    f_sub_2->get_qos(f_sub_qos_2);

    EXPECT_EQ(f_sub_qos.partition().size(), 0);
    EXPECT_EQ(f_sub_qos_2.partition().size(), 2);

    dal_type_support_t* ts =
        dal_create_type_support((void*)(mj_interfaces::msg::Mjtype::FullyQualifiedName()));
    ret = dal_type_support_register_type(ts, participant, dal_type_support_get_type_name(ts));

    // topic
    dal_topic_t* topic = dal_domain_participant_create_topic(participant,
                                                             "HelloWorldTopic",
                                                             dal_type_support_get_type_name(ts),
                                                             DAL_TOPIC_QOS_DEFAULT,
                                                             NULL,
                                                             DAL_ANY_STATUS);

    // writer
    dal_datawriter_qos_t* d_writer_qos = dal_create_default_datawriter_qos();
    EXPECT_EQ(d_writer_qos->durability.kind, DAL_TRANSIENT_LOCAL_DURABILITY_QOS);
    EXPECT_EQ(d_writer_qos->reliability.kind, DAL_RELIABLE_RELIABILITY_QOS);

    dal_datawriter_t* writer = dal_publisher_create_datawriter(
        pub, topic, DAL_DATAWRITER_QOS_DEFAULT, NULL, DAL_ANY_STATUS);
    EXPECT_NE(writer->handle_, nullptr);

    auto writer_impl = reinterpret_cast<DalDataWriterImpl*>(writer->handle_);
    auto f_writer = writer_impl->FastddsDataWriter();

    DataWriterQos f_writer_qos;
    f_writer->get_qos(f_writer_qos);

    EXPECT_EQ(f_writer_qos.durability().kind, eprosima::fastrtps::TRANSIENT_LOCAL_DURABILITY_QOS);
    EXPECT_EQ(f_writer_qos.reliability().kind, eprosima::fastrtps::RELIABLE_RELIABILITY_QOS);
    EXPECT_EQ(f_writer_qos.reliability().max_blocking_time.to_ns(), 100000000);

    dal_datawriter_t* writer_2 = dal_publisher_create_datawriter_with_profile(
        pub, topic, DAL_DATAWRITER_QOS_DEFAULT, "datawriter_profile_bbb", NULL, DAL_ANY_STATUS);
    EXPECT_NE(writer_2->handle_, nullptr);

    auto writer_impl_2 = reinterpret_cast<DalDataWriterImpl*>(writer_2->handle_);
    auto f_writer_2 = writer_impl_2->FastddsDataWriter();

    DataWriterQos f_writer_qos_2;
    f_writer_2->get_qos(f_writer_qos_2);

    EXPECT_EQ(f_writer_qos_2.durability().kind, eprosima::fastrtps::VOLATILE_DURABILITY_QOS);
    EXPECT_EQ(f_writer_qos_2.reliability().kind, eprosima::fastrtps::BEST_EFFORT_RELIABILITY_QOS);
    EXPECT_EQ(f_writer_qos_2.reliability().max_blocking_time.to_ns(), 1000856000);

    // reader
    dal_datareader_qos_t* d_reader_qos = dal_create_default_datareader_qos();
    EXPECT_EQ(d_reader_qos->durability.kind, DAL_VOLATILE_DURABILITY_QOS);
    EXPECT_EQ(d_reader_qos->reliability.kind, DAL_BEST_EFFORT_RELIABILITY_QOS);

    dal_datareader_t* reader = dal_subscriber_create_datareader(
        sub, topic, DAL_DATAREADER_QOS_DEFAULT, NULL, DAL_ANY_STATUS);
    EXPECT_NE(reader->handle_, nullptr);

    auto reader_impl = reinterpret_cast<DalDataReaderImpl*>(reader->handle_);
    auto f_reader = reader_impl->FastddsDataReader();

    DataReaderQos f_reader_qos;
    f_reader->get_qos(f_reader_qos);

    EXPECT_EQ(f_reader_qos.durability().kind, eprosima::fastrtps::VOLATILE_DURABILITY_QOS);
    EXPECT_EQ(f_reader_qos.reliability().kind, eprosima::fastrtps::BEST_EFFORT_RELIABILITY_QOS);
    EXPECT_EQ(f_reader_qos.reliability().max_blocking_time.to_ns(), 100000000);

    dal_datareader_t* reader_2 = dal_subscriber_create_datareader_with_profile(
        sub, topic, DAL_DATAREADER_QOS_DEFAULT, "datareader_profile_bbb", NULL, DAL_ANY_STATUS);
    EXPECT_NE(reader_2->handle_, nullptr);

    auto reader_impl_2 = reinterpret_cast<DalDataReaderImpl*>(reader_2->handle_);
    auto f_reader_2 = reader_impl_2->FastddsDataReader();

    DataReaderQos f_reader_qos_2;
    f_reader_2->get_qos(f_reader_qos_2);

    EXPECT_EQ(f_reader_qos_2.durability().kind, eprosima::fastrtps::TRANSIENT_LOCAL_DURABILITY_QOS);
    EXPECT_EQ(f_reader_qos_2.reliability().kind, eprosima::fastrtps::RELIABLE_RELIABILITY_QOS);
    EXPECT_EQ(f_reader_qos_2.reliability().max_blocking_time.to_ns(), 1000856000);
}

TEST(config_loader_api_takes_effect, test_config_loader)
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    ret = dal_load_config_from_path("dal_fastdds_profiles.xml", nullptr);
    EXPECT_EQ(ret, DAL_RETCODE_OK);
    EXPECT_EQ(ConfigManager::GetInstance()->getConfigType(),
              ConfigManager::ConfigType::CONFIG_TYPE_SET_BY_API);

    test_config_loader();
}

TEST(config_loader_env_takes_effect, test_config_loader)
{
    dal_ret_t ret = DAL_RETCODE_ERROR;
    setenv("DAL_CONFIG_PATH", "dal_fastdds_profiles.xml", 1);
    ret = dal_load_config_from_path("dal_fastdds_profiles_wrong.xml", nullptr);
    EXPECT_EQ(ret, DAL_RETCODE_OK);
    EXPECT_EQ(ConfigManager::GetInstance()->getConfigType(),
              ConfigManager::ConfigType::CONFIG_TYPE_SET_BY_ENV);

    test_config_loader();
}